/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file spi_drv.c
 * @brief This file defines spi driver functions
 */

/*******Includes***************************************************************/
#include "spi_reg_access.h"
#include "clock_drv.h"
#include "dma_drv.h"

/*******Definitions************************************************************/
#define SPI_USE_DMA_CIRCULAR_MODE (1U)
/*******Variables***'***********************************************************/
/*! @brief Table to save register base addresses for spi instances */
static SPI_Type* s_spiRegBase[CONFIG_SPI_INSTANCE_COUNT] = CONFIG_SPI_REG_BASE_PTRS;

/*! @brief Table to save module names for spi instances */
static module_names_t s_spiModuleName[CONFIG_SPI_INSTANCE_COUNT] = CONFIG_SPI_MODULE_NAMES;

/*! @brief Table to save interrupt ids for spi instances */
static IRQn_Type s_spiIrqId[CONFIG_SPI_INSTANCE_COUNT] = CONFIG_SPI_IRQS;

/*! @brief Table to save state for spi instances */
static spi_state_t* s_spiState[CONFIG_SPI_INSTANCE_COUNT] = { NULL };

/*! @brief Variable to save spi dummy read data */
static uint32_t s_rxDummyData = 0xffffffffU;

/*! @brief Table to save transmit dma requests for spi instances */
static dma_perial_request_t s_spiTxDmaRequest[CONFIG_SPI_INSTANCE_COUNT] = CONFIG_SPI_TX_DMA_REQS;

/*! @brief Table to save receive dma requests for spi instances */
static dma_perial_request_t s_spiRxDmaRequest[CONFIG_SPI_INSTANCE_COUNT] = CONFIG_SPI_RX_DMA_REQS;

/*! @brief Table to Save the linked list by tx channel */
static dma_linked_list_item_t s_txLinkedList[CONFIG_SPI_INSTANCE_COUNT][CONFIG_SPI_DMA_MAX_LINK];

/*! @brief Table to save state for rx channel */
static dma_linked_list_item_t s_rxLinkedList[CONFIG_SPI_INSTANCE_COUNT][CONFIG_SPI_DMA_MAX_LINK];

/*******Prototypes*************************************************************/
static void SPI_TxDmaCallback(uint8_t channel, dma_event_t event, void* param);
static void SPI_RxDmaCallback(uint8_t channel, dma_event_t event, void* param);

/*******Private Functions******************************************************/

/*******************************************************************************
 *** Function Name : SPI_MasterStoreRuntimeParam
 *** Description   : Store runtime paramaters in matser mode
 ******************************************************************************/
static void SPI_MasterStoreRuntimeParam(uint32_t instance,
                                        const spi_master_config_t* masterConfig)
{
    spi_state_t* spiState = s_spiState[instance];

    /* Configure internal state structure for SPI */
#if (CONFIG_SPI_NEW_FRAMESIZE_FEATURE == 1U)

    if (masterConfig->frameSize == SPI_FRAME_SIZE_8BIT) {
        spiState->bytesPerFrame = 1U;
    } else if (masterConfig->frameSize == SPI_FRAME_SIZE_16BIT) {
        spiState->bytesPerFrame = 2U;
    } else {
        spiState->bytesPerFrame = 4U;
    }

#else

    if (masterConfig->frameSize == SPI_FRAME_SIZE_8BIT) {
        spiState->bytesPerFrame = 1U;
    } else {
        spiState->bytesPerFrame = 2U;
    }

#endif
    spiState->frameSize = masterConfig->frameSize;
    spiState->isContinuousMode = masterConfig->isContinuousMode;
    spiState->isMaster = true;
    spiState->isBlocking = false;
    spiState->baudConfig = masterConfig->baudConfig;
    spiState->isTransferInProgress = false;
    spiState->txOrder = masterConfig->txOrder;
    spiState->rxOrder = masterConfig->rxOrder;
    spiState->transferType = masterConfig->transferType;
    spiState->txDMAChannel = masterConfig->txDMAChannel;
    spiState->rxDMAChannel = masterConfig->rxDMAChannel;
    spiState->callback = masterConfig->callback;
    spiState->callbackParam = masterConfig->callbackParam;
    spiState->txDummyData = 0xffffffffU;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveStoreRuntimeParam
 *** Description   : Store runtime paramaters in slave mode
 ******************************************************************************/
static void SPI_SlaveStoreRuntimeParam(uint32_t instance,
                                       const spi_slave_config_t* slaveConfig)
{
    spi_state_t* spiState = s_spiState[instance];

    /* Configure internal state structure for SPI */
#if (CONFIG_SPI_NEW_FRAMESIZE_FEATURE == 1U)

    if (slaveConfig->frameSize == SPI_FRAME_SIZE_8BIT) {
        spiState->bytesPerFrame = 1U;
    } else if (slaveConfig->frameSize == SPI_FRAME_SIZE_16BIT) {
        spiState->bytesPerFrame = 2U;
    } else {
        spiState->bytesPerFrame = 4U;
    }

#else

    if (slaveConfig->frameSize == SPI_FRAME_SIZE_8BIT) {
        spiState->bytesPerFrame = 1U;
    } else {
        spiState->bytesPerFrame = 2U;
    }

#endif
    spiState->frameSize = slaveConfig->frameSize;
    spiState->isMaster = false;
    spiState->isBlocking = false;
    spiState->isTransferInProgress = false;
    spiState->txOrder = slaveConfig->txOrder;
    spiState->rxOrder = slaveConfig->rxOrder;
    spiState->transferType = slaveConfig->transferType;
    spiState->txDMAChannel = slaveConfig->txDMAChannel;
    spiState->rxDMAChannel = slaveConfig->rxDMAChannel;
    spiState->callback = slaveConfig->callback;
    spiState->callbackParam = slaveConfig->callbackParam;
    spiState->txDummyData = 0xffffffffU;
}

/*******************************************************************************
 *** Function Name : SPI_FillTxBuffer
 *** Description   : Filling data to transmit fifo
 ******************************************************************************/
static void SPI_FillTxBuffer(uint32_t instance)
{
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];
    uint32_t frameToSend = 0U;
    uint16_t i = 0U;

    if (spiState->txBuff != NULL) {
        for (i = 0U; i < spiState->bytesPerFrame; i++) {
            frameToSend |= (uint32_t)(*(spiState->txBuff) << (i << 3U));
            spiState->txBuff++;
        }
    } else {
        frameToSend |= spiState->txDummyData;
    }

    SPI_REG_WriteData(regBase, frameToSend);

    spiState->txSize -= spiState->bytesPerFrame;
}

/*******************************************************************************
 *** Function Name : SPI_ReadRxBuffer
 *** Description   : Reading data from receive fifo
 ******************************************************************************/
static void SPI_ReadRxBuffer(uint32_t instance)
{
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];
    uint32_t receivedFrame = 0U;
    uint16_t i = 0U;

    receivedFrame = SPI_REG_ReadData(regBase);

    if (spiState->rxBuff != NULL) {
        for (i = 0U; i < spiState->bytesPerFrame; i++) {
            *(spiState->rxBuff) = (uint8_t)(receivedFrame >> (i << 3U));
            spiState->rxBuff++;
        }
    } else {
        (void)receivedFrame;
    }

    spiState->rxSize -= spiState->bytesPerFrame;
}


void SPI_DMATransfer(uint32_t instance, uint32_t transferSize, const uint8_t* txBuffer, uint8_t* rxBuffer)
{
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];

    dma_transfer_width_t dmaTransferWidth = DMA_TRANSFER_WIDTH_8BIT;
    uint32_t dmaTransferCount = 0U;

#if (CONFIG_SPI_NEW_FRAMESIZE_FEATURE == 1U)

    if (spiState->frameSize == SPI_FRAME_SIZE_8BIT) {
        dmaTransferWidth = DMA_TRANSFER_WIDTH_8BIT;
        dmaTransferCount = transferSize;
    } else if (spiState->frameSize == SPI_FRAME_SIZE_16BIT) {
        dmaTransferWidth = DMA_TRANSFER_WIDTH_16BIT;
        dmaTransferCount = transferSize >> 1U;
    } else {
        dmaTransferWidth = DMA_TRANSFER_WIDTH_32BIT;
        dmaTransferCount = transferSize >> 2U;
    }

#else

    if (spiState->frameSize == SPI_FRAME_SIZE_8BIT) {
        dmaTransferWidth = DMA_TRANSFER_WIDTH_8BIT;
        dmaTransferCount = transferSize;
    } else {
        dmaTransferWidth = DMA_TRANSFER_WIDTH_16BIT;
        dmaTransferCount = transferSize >> 1U;
    }

#endif

    if ((transferSize > 4095) || (txBuffer == NULL) || (rxBuffer == NULL)) {
        uint32_t buffOffset = 0U;
        uint8_t LinklistNum = 0U;
        uint8_t txListNode = 0U;
        uint8_t rxListNode = 0U;
        LinklistNum = (uint8_t)((dmaTransferCount / DMA_CHANNEL_TRANFER_MAX_SIZE) + (((dmaTransferCount % DMA_CHANNEL_TRANFER_MAX_SIZE) == 0) ? 0 : 1));

        for (txListNode = 0U; txListNode < LinklistNum; txListNode++) {
            s_txLinkedList[instance][txListNode].control.lliITCEnable = false;

            if (txBuffer != NULL) {
                s_txLinkedList[instance][txListNode].control.srcAddressIncrement = true;
                s_txLinkedList[instance][txListNode].srcAddress = (uint32_t)(txBuffer + buffOffset);
            } else {
                s_txLinkedList[instance][txListNode].control.srcAddressIncrement = false;
                s_txLinkedList[instance][txListNode].srcAddress = (uint32_t)(&(spiState->txDummyData));
            }

            s_txLinkedList[instance][txListNode].control.destAddressIncrement = false;
            s_txLinkedList[instance][txListNode].control.destTransferWidth = (uint32_t)dmaTransferWidth;

            s_txLinkedList[instance][txListNode].control.srcTransferWidth = (uint32_t)dmaTransferWidth;
            s_txLinkedList[instance][txListNode].destAddress = (uint32_t)SPI_REG_GetDataAddress(regBase);

            if (txListNode < (LinklistNum - 1)) {
                s_txLinkedList[instance][txListNode].control.transferSize = DMA_CHANNEL_TRANFER_MAX_SIZE;
                buffOffset += (DMA_CHANNEL_TRANFER_MAX_SIZE * spiState->bytesPerFrame);
                s_txLinkedList[instance][txListNode].next = &s_txLinkedList[instance][txListNode + 1];
            } else {
                s_txLinkedList[instance][txListNode].control.transferSize = dmaTransferCount - (DMA_CHANNEL_TRANFER_MAX_SIZE * txListNode);
                s_txLinkedList[instance][txListNode].control.lliITCEnable = true;
                s_txLinkedList[instance][txListNode].next = &s_txLinkedList[instance][0];
            }
        }

        buffOffset = 0U;
        uint32_t* rxDummyBuffer = NULL;
        rxDummyBuffer = &s_rxDummyData;

        for (rxListNode = 0U; rxListNode < LinklistNum; rxListNode++) {
            s_rxLinkedList[instance][rxListNode].control.lliITCEnable = false;

            if (rxBuffer != NULL) {
                s_rxLinkedList[instance][rxListNode].control.destAddressIncrement = true;
                s_rxLinkedList[instance][rxListNode].destAddress = (uint32_t)(rxBuffer + buffOffset);
            } else {
                s_rxLinkedList[instance][rxListNode].control.destAddressIncrement = false;
                s_rxLinkedList[instance][rxListNode].destAddress = (uint32_t)(rxDummyBuffer);
            }

            s_rxLinkedList[instance][rxListNode].control.srcAddressIncrement = false;
            s_rxLinkedList[instance][rxListNode].control.destTransferWidth = (uint32_t)dmaTransferWidth;

            s_rxLinkedList[instance][rxListNode].control.srcTransferWidth = (uint32_t)dmaTransferWidth;
            s_rxLinkedList[instance][rxListNode].srcAddress = (uint32_t)SPI_REG_GetDataAddress(regBase);

            if (rxListNode < (LinklistNum - 1)) {
                s_rxLinkedList[instance][rxListNode].control.transferSize = DMA_CHANNEL_TRANFER_MAX_SIZE;
                buffOffset += (DMA_CHANNEL_TRANFER_MAX_SIZE * spiState->bytesPerFrame);
                s_rxLinkedList[instance][rxListNode].next = &s_rxLinkedList[instance][rxListNode + 1];
            } else {
                s_rxLinkedList[instance][rxListNode].control.transferSize = dmaTransferCount - DMA_CHANNEL_TRANFER_MAX_SIZE * (rxListNode);
                s_rxLinkedList[instance][rxListNode].control.lliITCEnable = true;
                s_rxLinkedList[instance][rxListNode].next = &s_rxLinkedList[instance][0];
            }
        }

        DMA_ConfigLinkedListTransfer(spiState->txDMAChannel, DMA_TRANSFER_MEM2PERIPH,
                                     s_spiTxDmaRequest[instance], s_txLinkedList[instance]);
        DMA_ConfigLinkedListTransfer(spiState->rxDMAChannel, DMA_TRANSFER_PERIPH2MEM,
                                     s_spiRxDmaRequest[instance], s_rxLinkedList[instance]);
    } else {
#if (SPI_USE_DMA_CIRCULAR_MODE == 1U)
        s_txLinkedList[instance][0].control.srcAddressIncrement = true;
        s_txLinkedList[instance][0].srcAddress = (uint32_t)(txBuffer);
        s_txLinkedList[instance][0].control.destAddressIncrement = false;
        s_txLinkedList[instance][0].control.destTransferWidth = (uint32_t)dmaTransferWidth;
        s_txLinkedList[instance][0].control.srcTransferWidth = (uint32_t)dmaTransferWidth;
        s_txLinkedList[instance][0].destAddress = (uint32_t)SPI_REG_GetDataAddress(regBase);
        s_txLinkedList[instance][0].control.transferSize = dmaTransferCount;
        s_txLinkedList[instance][0].control.lliITCEnable = true;
        s_txLinkedList[instance][0].next = &s_txLinkedList[instance][0];

        s_rxLinkedList[instance][0].control.destAddressIncrement = true;
        s_rxLinkedList[instance][0].destAddress = (uint32_t)(rxBuffer);
        s_rxLinkedList[instance][0].control.srcAddressIncrement = false;
        s_rxLinkedList[instance][0].control.destTransferWidth = (uint32_t)dmaTransferWidth;
        s_rxLinkedList[instance][0].control.srcTransferWidth = (uint32_t)dmaTransferWidth;
        s_rxLinkedList[instance][0].srcAddress = (uint32_t)SPI_REG_GetDataAddress(regBase);
        s_rxLinkedList[instance][0].control.transferSize = dmaTransferCount;
        s_rxLinkedList[instance][0].control.lliITCEnable = true;
        s_rxLinkedList[instance][0].next = &s_rxLinkedList[instance][0];;

        DMA_ConfigLinkedListTransfer(spiState->txDMAChannel, DMA_TRANSFER_MEM2PERIPH,
                                     s_spiTxDmaRequest[instance], s_txLinkedList[instance]);
        DMA_ConfigLinkedListTransfer(spiState->rxDMAChannel, DMA_TRANSFER_PERIPH2MEM,
                                     s_spiRxDmaRequest[instance], s_rxLinkedList[instance]);
#else
        DMA_ConfigSingleTransfer(spiState->txDMAChannel,
                                 DMA_TRANSFER_MEM2PERIPH,
                                 (uint32_t)txBuffer,
                                 SPI_REG_GetDataAddress(regBase),
                                 dmaTransferWidth,
                                 dmaTransferCount,
                                 s_spiTxDmaRequest[instance]);
        DMA_ConfigSingleTransfer(spiState->rxDMAChannel,
                                 DMA_TRANSFER_PERIPH2MEM,
                                 SPI_REG_GetDataAddress(regBase),
                                 (uint32_t)rxBuffer,
                                 dmaTransferWidth,
                                 dmaTransferCount,
                                 s_spiRxDmaRequest[instance]);
#endif
    }

    DMA_InstallCallback(spiState->txDMAChannel, SPI_TxDmaCallback, (void*)(instance));
    DMA_InstallCallback(spiState->rxDMAChannel, SPI_RxDmaCallback, (void*)(instance));
    DMA_ConfigureInt(spiState->txDMAChannel, DMA_CHN_COMPLETE_INT, true);
    DMA_ConfigureInt(spiState->rxDMAChannel, DMA_CHN_COMPLETE_INT, true);

    SPI_REG_SetRxDmaEnable(regBase, true);
    SPI_REG_SetTxDmaEnable(regBase, true);

    /* Rx channel should start before Tx channel to avoid overflow */
    DMA_StartChannel(spiState->rxDMAChannel);
    DMA_StartChannel(spiState->txDMAChannel);
}

/*******************************************************************************
 *** Function Name : SPI_MasterStartTransfer
 *** Description   : Start transferring in master mode
 ******************************************************************************/
static errcode_t SPI_MasterStartTransfer(uint32_t instance,
                                         const uint8_t* txBuffer,
                                         uint8_t* rxBuffer,
                                         uint32_t transferSize)
{
    errcode_t errCode = ERR_SUCCESS;
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];
    uint32_t alignedSize = (spiState->bytesPerFrame - 1U);

    if ((((uint32_t)txBuffer & alignedSize) != 0U) \
            || (((uint32_t)rxBuffer & alignedSize) != 0U) \
            || ((transferSize & alignedSize) != 0U)) {
        errCode = ERR_ERROR;
        /* txBuffer(for M0 core) and transferSize should be frame size aligned */
    } else if ((spiState->transferType == SPI_USING_DMA) &&
               (transferSize > (CONFIG_SPI_DMA_MAX_SIZE << alignedSize))) {
        /* DMA transfer count over than DMA_CHANNEL_TRANFER_MAX_SIZE is unsuported */
        errCode = ERR_UNSUPPORTED;
    } else {
        spiState->status = SPI_TRANSFER_OK;

        /* Clear fifo and all status flags */
        SPI_REG_ClearErrorStatusFlag(regBase, ((uint32_t)(SPI_STATUS_TX_UNDERFLOW) | (uint32_t)(SPI_STATUS_RX_OVERFLOW)));

        while (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            (void)SPI_REG_ReadData(regBase);
        }

        /* Enable error interrupt sources */
        SPI_REG_SetErrorIntEnable(regBase, ((uint32_t)(SPI_INT_TX_UNDERFLOW) | (uint32_t)(SPI_INT_RX_OVERFLOW)), true);

        spiState->txBuff = txBuffer;
        spiState->rxBuff = rxBuffer;
        spiState->txSize = transferSize;
        spiState->rxSize = transferSize;
        spiState->isTransferInProgress = true;

        if (spiState->transferType == SPI_USING_INTERRUPT) {
            SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, false);
            SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, true);
            SPI_FillTxBuffer(instance);
        } else {
            SPI_DMATransfer(instance, transferSize, txBuffer, rxBuffer);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_CompleteTransfer
 *** Description   : Settings after transfer complete
 ******************************************************************************/
static void SPI_CompleteTransfer(uint32_t instance)
{
    spi_state_t* spiState = s_spiState[instance];
#if (SPI_USE_DMA_CIRCULAR_MODE == 0U)
    SPI_Type* regBase = s_spiRegBase[instance];
#endif

    /* Check if transfer is still in progress */
    if (spiState->isTransferInProgress) {
#if (SPI_USE_DMA_CIRCULAR_MODE == 0U)
        spiState->isTransferInProgress = false;

        if (spiState->transferType == SPI_USING_INTERRUPT) {
            /* Disable interrupt requests */
            if (spiState->isMaster != true) {
                SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, false);
            }

            SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, false);
        } else if (spiState->transferType == SPI_USING_DMA) {
            /* Disable DMA requests */
            SPI_REG_SetTxDmaEnable(regBase, false);
            SPI_REG_SetRxDmaEnable(regBase, false);

            /* Stop DMA channels */
            DMA_StopChannel(spiState->txDMAChannel);
            DMA_StopChannel(spiState->rxDMAChannel);
        } else {

        }

        /* Disable the interrupts and clear the status flags for transmit/receive errors */
        SPI_REG_SetIntEnable(regBase, SPI_INT_TX_UNDERFLOW, false);
        SPI_REG_SetIntEnable(regBase, SPI_INT_RX_OVERFLOW, false);
        SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW);
        SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW);

        if (spiState->isBlocking) {
            (void)OS_Adapter_PostSemaphore(&(spiState->spiSemaphore));
            spiState->isBlocking = false;
        }

        if (spiState->isMaster && spiState->isContinuousMode) {
            SPI_REG_ReleaseCS(regBase);
        }
#endif
        if (spiState->callback != NULL) {
            switch (spiState->status) {
                case SPI_TRANSFER_OK:
                    spiState->callback(instance, SPI_EVENT_END_TRANSFER, spiState->callbackParam);
                    break;

                case SPI_TRANSMIT_ERROR:
                    spiState->callback(instance, SPI_EVENT_TX_ERROR, spiState->callbackParam);
                    break;

                case SPI_RECEIVE_ERROR:
                    spiState->callback(instance, SPI_EVENT_RX_ERROR, spiState->callbackParam);
                    break;

                default:
                    break;
            }
        }
    }
}

/*******************************************************************************
 *** Function Name : SPI_MasterIRQHandlerProcess
 *** Description   : SPI master interrupt handler process function
 ******************************************************************************/
static void SPI_MasterIRQHandlerProcess(uint32_t instance)
{
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];

    do {
        /* If error is detected, transfer will be aborted */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW)) {
            spiState->status = SPI_TRANSMIT_ERROR;
            SPI_CompleteTransfer(instance);
            SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW);
            break;
        }

        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW)) {
            spiState->status = SPI_RECEIVE_ERROR;
            SPI_CompleteTransfer(instance);
            SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW);
            break;
        }

        /* Receiving data */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            if (spiState->rxSize > 0U) {
                SPI_ReadRxBuffer(instance);
            } else {
                SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, false);
            }
        }

        /* Transmitting data */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_NOT_FULL)) {
            if (spiState->txSize > 0U) {
                SPI_FillTxBuffer(instance);
            }
        }

        /* Check if transfer is completed */
        if ((spiState->txSize == 0U) && (spiState->rxSize == 0U)) {
            SPI_CompleteTransfer(instance);
        }
    } while (false);
}

/*******************************************************************************
 *** Function Name : SPI_SlaveIRQHandlerProcess
 *** Description   : SPI slave interrupt handler process function
 ******************************************************************************/
static void SPI_SlaveIRQHandlerProcess(uint32_t instance)
{
    SPI_Type* regBase = s_spiRegBase[instance];
    spi_state_t* spiState = s_spiState[instance];

    do {
        /* If error is detected, transfer will be aborted */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW)) {
            spiState->status = SPI_TRANSMIT_ERROR;
            SPI_CompleteTransfer(instance);
            break;
        }

        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW)) {
            spiState->status = SPI_RECEIVE_ERROR;
            SPI_CompleteTransfer(instance);
            break;
        }

        /* Receiving data */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            if (spiState->rxSize > 0U) {
                SPI_ReadRxBuffer(instance);
            } else {
                SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, false);
            }
        }

        /* Transmitting data */
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_NOT_FULL)) {
            if (spiState->txSize > 0U) {
                SPI_FillTxBuffer(instance);
            } else {
                SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, false);
            }
        }

        /* Check if transfer is completed */
        if ((spiState->txSize == 0U) && (spiState->rxSize == 0U)) {
            SPI_CompleteTransfer(instance);
        }
    } while (false);
}

/*******************************************************************************
 *** Function Name : SPI_DmaCallback
 *** Description   : Dma callback function
 ******************************************************************************/
static void SPI_TxDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    spi_state_t* spiState = s_spiState[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
#if (SPI_USE_DMA_CIRCULAR_MODE == 1U)
	SPI_CompleteTransfer(instance);
#else
        spiState->txSize = 0U;

        if (spiState->rxSize == 0U) {
            SPI_CompleteTransfer(instance);
        }
#endif
    } else {
        spiState->status = SPI_TRANSMIT_ERROR;

        SPI_CompleteTransfer(instance);
    }
}

/*******************************************************************************
 *** Function Name : SPI_RxDmaCallback
 *** Description   : Rx dma callback function
 ******************************************************************************/
static void SPI_RxDmaCallback(uint8_t channel, dma_event_t event, void* param)
{
    (void)channel;
    uint32_t instance = ((uint32_t)param);
    spi_state_t* spiState = s_spiState[instance];

    if (event == DMA_EVENT_TRANFER_COMPLETE) {
#if (SPI_USE_DMA_CIRCULAR_MODE == 1U)
	SPI_CompleteTransfer(instance);
#else
        spiState->rxSize = 0U;

        if (spiState->txSize == 0U) {
            SPI_CompleteTransfer(instance);
        }
#endif
    } else {
        spiState->status = SPI_RECEIVE_ERROR;

        SPI_CompleteTransfer(instance);
    }
}

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : SPI_MasterInit
 *** Description   : Init SPI instance as a master
 ******************************************************************************/
errcode_t SPI_MasterInit(uint32_t instance,
                         spi_state_t* spiState,
                         const spi_master_config_t* masterConfig)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(spiState != NULL);
    ASSERT_PARAM(masterConfig != NULL);

    errcode_t errCode = ERR_SUCCESS;
    SPI_Type* regBase = s_spiRegBase[instance];
    module_names_t spiModName = s_spiModuleName[instance];

    /* Enable module bus clock */
    CLOCK_SetModuleBusClockEnable(spiModName, true);
    /* Set spi instance in-active */
    CLOCK_SetModuleSoftResetState(spiModName, RESET_INACTIVE);

    s_spiState[instance] = spiState;
    SPI_MasterStoreRuntimeParam(instance, masterConfig);

    /* Delay and timing parameters for master mode need be configured in disabled state */
    errCode = SPI_REG_Disable(regBase);

    if (errCode == ERR_SUCCESS) {
        SPI_REG_SetMasterSlaveMode(regBase, SPI_MODE_MASTER);
        SPI_REG_SetPinConfig(regBase, masterConfig->pinConfig);
        SPI_REG_SetContinuousMode(regBase, masterConfig->isContinuousMode);

        /* Set baudrate */
        SPI_REG_SetSckHighTime(regBase, masterConfig->baudConfig.sckHigh);
        SPI_REG_SetSckLowTime(regBase, masterConfig->baudConfig.sckLow);

        /* Set chip select timing parameters */
        SPI_REG_SetCsSetupTime(regBase, masterConfig->csTiming.csSetup);
        SPI_REG_SetCsHoldTime(regBase, masterConfig->csTiming.csHold);
        SPI_REG_SetCsIdleTime(regBase, masterConfig->csTiming.csIdle);

        SPI_REG_SetCsPolarity(regBase, masterConfig->csPol);
        SPI_REG_SetClockPolarity(regBase, masterConfig->clkPol);
        SPI_REG_SetClockPhase(regBase, masterConfig->clkPha);
        SPI_REG_SetFrameSize(regBase, masterConfig->frameSize);
        SPI_REG_SetTransmitOrder(regBase, masterConfig->txOrder);
        SPI_REG_SetReceiveOrder(regBase, masterConfig->rxOrder);
        SPI_REG_SetSampleDelay(regBase, masterConfig->sampleDelay);

        /* Create the semaphore */
        errCode = OS_Adapter_CreateSemaphore(&(spiState->spiSemaphore), 0U);

        /* Enable spi interrupt */
        NVIC_EnableIRQ(s_spiIrqId[instance]);

        SPI_REG_Enable(regBase);

        OS_Adapter_Init(NULL);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterDeinit
 *** Description   : De-Init a SPI instance
 ******************************************************************************/
errcode_t SPI_MasterDeinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(s_spiState[instance] != NULL);

    errcode_t errCode = ERR_SUCCESS;
    spi_state_t* spiState = s_spiState[instance];
    module_names_t spiModName = s_spiModuleName[instance];
    IRQn_Type spiIrqId = s_spiIrqId[instance];

#if (SPI_USE_DMA_CIRCULAR_MODE == 0U)
    ASSERT_PARAM(spiState->isTransferInProgress == false);
#endif

    /* Destroy the semaphore */
#ifdef USING_OS_FREERTOS
    if (spiState->spiSemaphore != NULL) {
#endif
        (void)OS_Adapter_DestroySemaphore(&(spiState->spiSemaphore));
#ifdef USING_OS_FREERTOS
        spiState->spiSemaphore = NULL;
    }
#endif

    /* Clear the state pointer */
    s_spiState[instance] = NULL;

    /* Set spi instance active */
    CLOCK_SetModuleSoftResetState(spiModName, RESET_ACTIVE);
    /* Disable module bus clock */
    CLOCK_SetModuleBusClockEnable(spiModName, false);

    /* Disable spi interrupt */
    NVIC_DisableIRQ(spiIrqId);
    NVIC_ClearPendingIRQ(spiIrqId);

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterGetDefaultConfig
 *** Description   : Get a default configuration of SPI master
 ******************************************************************************/
void SPI_MasterGetDefaultConfig(spi_master_config_t* masterConfig)
{
    ASSERT_PARAM(masterConfig != NULL);

    masterConfig->isContinuousMode = false;
    masterConfig->pinConfig = SPI_PIN_SOUT_MISO_SIN_MOSI;
    masterConfig->baudConfig.sckHigh = 5U;
    masterConfig->baudConfig.sckLow = 5U;
    masterConfig->csTiming.csSetup = 5U;
    masterConfig->csTiming.csHold = 5U;
    masterConfig->csTiming.csIdle = 5U;
    masterConfig->csPin = SPI_CS0;
    masterConfig->csPol = SPI_CS_POL_LOW;
    masterConfig->clkPol = SPI_CLK_POL_LOW;
    masterConfig->clkPha = SPI_CLK_PHA_1ST;
    masterConfig->frameSize = SPI_FRAME_SIZE_8BIT;
    masterConfig->txOrder = SPI_TRANSFER_ORDER_MSB;
    masterConfig->rxOrder = SPI_TRANSFER_ORDER_MSB;
    masterConfig->sampleDelay = SPI_SAMPLE_DELAY_NONE;
    masterConfig->transferType = SPI_USING_INTERRUPT;
    masterConfig->txDMAChannel = 0U;
    masterConfig->rxDMAChannel = 1U;
    masterConfig->callback = NULL;
    masterConfig->callbackParam = NULL;
}

/*******************************************************************************
 *** Function Name : SPI_MasterTransferBlocking
 *** Description   : Start a blocking transfer for SPI master
 ******************************************************************************/
errcode_t SPI_MasterTransferBlocking(uint32_t instance,
                                     const uint8_t* txBuffer,
                                     uint8_t* rxBuffer,
                                     uint32_t transferSize,
                                     uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));

    errcode_t errCode = ERR_SUCCESS;
    SPI_Type* regBase = s_spiRegBase[instance];
    spi_state_t* spiState = s_spiState[instance];

    if (transferSize > 0U) {
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_MODULE_BUSY)) {
            errCode = ERR_BUSY;
        } else {
            /* Dummy wait to ensure the senmaphore is 0, no need to check result */
            (void)OS_Adapter_WaitSemaphore(&(spiState->spiSemaphore), 0U);
            spiState->isBlocking = true;

            errCode = SPI_MasterStartTransfer(instance, txBuffer, rxBuffer, transferSize);

            if (errCode == ERR_SUCCESS) {
                errCode = OS_Adapter_WaitSemaphore(&(spiState->spiSemaphore), timeout);

                if (errCode == ERR_TIMEOUT) {
                    spiState->isBlocking = false;
                    SPI_MasterAbortTransfer(instance);
                } else {
                    /* Disable the interrupts and clear the status flags for transmit/receive errors */
                    SPI_REG_SetIntEnable(regBase, SPI_INT_TX_UNDERFLOW, false);
                    SPI_REG_SetIntEnable(regBase, SPI_INT_RX_OVERFLOW, false);
                    SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW);
                    SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW);
                }
            } else {
                /* Disable interrupt requests */
                SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, false);
                SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, false);

                spiState->isBlocking = false;
            }
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterTransfer
 *** Description   : Start a non-blocking transfer for SPI master
 ******************************************************************************/
errcode_t SPI_MasterTransfer(uint32_t instance,
                             const uint8_t* txBuffer,
                             uint8_t* rxBuffer,
                             uint32_t transferSize)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));
    SPI_Type* regBase = s_spiRegBase[instance];

    errcode_t errCode = ERR_SUCCESS;

    if (transferSize > 0U) {
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_MODULE_BUSY)) {
            errCode = ERR_BUSY;
        } else {
            errCode = SPI_MasterStartTransfer(instance, txBuffer, rxBuffer, transferSize);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterTransferPolling
 *** Description   : Start a polling transfer for SPI master
 ******************************************************************************/
errcode_t SPI_MasterTransferPolling(uint32_t instance,
                                    const uint8_t* txBuffer,
                                    uint8_t* rxBuffer,
                                    uint32_t transferSize)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));
    ASSERT_PARAM(transferSize > 0U);

    errcode_t errCode = ERR_SUCCESS;
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];
    uint32_t alignedSize = (spiState->bytesPerFrame - 1U);

    if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_MODULE_BUSY)) {
        errCode = ERR_BUSY;
    } else if ((((uint32_t)txBuffer & alignedSize) != 0U) \
               || (transferSize & alignedSize) != 0U) {
        /* txBuffer(for M0 core) and transferSize should be frame size aligned */
        errCode = ERR_ERROR;
    } else {
        spiState->status = SPI_TRANSFER_OK;

        /* Clear fifo and all status flags */
        SPI_REG_ClearErrorStatusFlag(regBase, ((uint32_t)(SPI_STATUS_TX_UNDERFLOW) | (uint32_t)(SPI_STATUS_RX_OVERFLOW)));

        while (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            (void)SPI_REG_ReadData(regBase);
        }

        spiState->txBuff = txBuffer;
        spiState->rxBuff = rxBuffer;
        spiState->txSize = transferSize;
        spiState->rxSize = transferSize;
        spiState->isTransferInProgress = true;

        do {
            /* Transmitting data */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_NOT_FULL)) {
                if (spiState->txSize > 0U) {
                    SPI_FillTxBuffer(instance);
                }
            }

            /* Receiving data */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
                if (spiState->rxSize > 0U) {
                    SPI_ReadRxBuffer(instance);
                }
            }

            /* Check the sending fifo for underflow */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW)) {
                errCode = ERR_SPI_TX_UNDERRUN;
                spiState->status = SPI_TRANSMIT_ERROR;
                break;
            }

            /* Check the receiving fifo for overflow */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW)) {
                errCode = ERR_SPI_RX_OVERFLOW;
                spiState->status = SPI_RECEIVE_ERROR;
                break;
            }
        } while ((spiState->txSize > 0U) || (spiState->rxSize > 0U));

        SPI_CompleteTransfer(instance);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterGetTransferStatus
 *** Description   : Get transfer status in non-blocking transfer:
 ***                 in progress or complete.
 ******************************************************************************/
errcode_t SPI_MasterGetTransferStatus(uint32_t instance, uint32_t* bytesRemained)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(s_spiState[instance] != NULL);

    const spi_state_t* spiState = s_spiState[instance];
    errcode_t errCode = ERR_SUCCESS;
    uint32_t dmaRemainingCount = 0U;

    if (bytesRemained != NULL) {
        if (spiState->transferType == SPI_USING_INTERRUPT) {
            *bytesRemained = spiState->rxSize;
        } else {
            dmaRemainingCount = DMA_GetRemainingCount(spiState->rxDMAChannel);
            *bytesRemained = dmaRemainingCount * spiState->bytesPerFrame;
        }
    }

    if (spiState->status == SPI_TRANSFER_OK) {
        errCode = (spiState->isTransferInProgress) ? ERR_BUSY : ERR_SUCCESS;
    } else if (spiState->status == SPI_TRANSMIT_ERROR) {
        errCode = ERR_SPI_TX_UNDERRUN;
    } else {
        errCode = ERR_SPI_RX_OVERFLOW;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterAbortTransfer
 *** Description   : Terminates a transfer during progress.
 ******************************************************************************/
void SPI_MasterAbortTransfer(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);

    SPI_CompleteTransfer(instance);
}

/*******************************************************************************
 *** Function Name : SPI_MasterSetDelay
 *** Description   : Set delay parameters
 ******************************************************************************/
errcode_t SPI_MasterSetDelay(uint32_t instance, const spi_cs_timing_t* csTiming)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(csTiming != NULL);

    SPI_Type* regBase = s_spiRegBase[instance];
    errcode_t errCode = ERR_SUCCESS;

    errCode = SPI_REG_Disable(regBase);

    if (errCode == ERR_SUCCESS) {
        SPI_REG_SetCsSetupTime(regBase, csTiming->csSetup);
        SPI_REG_SetCsHoldTime(regBase, csTiming->csHold);
        SPI_REG_SetCsIdleTime(regBase, csTiming->csIdle);

        SPI_REG_Enable(regBase);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_MasterSetCS
 *** Description   : Set chip select pin and set its polarity
 ******************************************************************************/
void SPI_MasterSetCS(uint32_t instance, spi_cs_t csNum, spi_cs_pol_t csPol)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);

    SPI_Type* regBase = s_spiRegBase[instance];

    SPI_REG_SetCsPolarity(regBase, csPol);
}

/*******************************************************************************
 *** Function Name : SPI_MasterReleaseCS
 *** Description   : De-Assert or release current chip select pin.
 ******************************************************************************/
void SPI_MasterReleaseCS(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);

    SPI_Type* regBase = s_spiRegBase[instance];

    SPI_REG_ReleaseCS(regBase);
}

/*******************************************************************************
 *** Function Name : SPI_SlaveInit
 *** Description   : Init SPI instance as a slave
 ******************************************************************************/
errcode_t SPI_SlaveInit(uint32_t instance,
                        spi_state_t* spiState,
                        const spi_slave_config_t* slaveConfig)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(spiState != NULL);
    ASSERT_PARAM(slaveConfig != NULL);

    errcode_t errCode = ERR_SUCCESS;
    SPI_Type* regBase = s_spiRegBase[instance];
    module_names_t spiModuleName = s_spiModuleName[instance];

    /* Enable module bus clock */
    CLOCK_SetModuleBusClockEnable(spiModuleName, true);
    /* Set spi instance in-active */
    CLOCK_SetModuleSoftResetState(spiModuleName, RESET_INACTIVE);

    s_spiState[instance] = spiState;
    SPI_SlaveStoreRuntimeParam(instance, slaveConfig);

    SPI_REG_SetMasterSlaveMode(regBase, SPI_MODE_SLAVE);
    SPI_REG_SetPinConfig(regBase, slaveConfig->pinConfig);
    SPI_REG_SetCsPolarity(regBase, slaveConfig->csPol);
    SPI_REG_SetClockPolarity(regBase, slaveConfig->clkPol);
    SPI_REG_SetClockPhase(regBase, slaveConfig->clkPha);
    SPI_REG_SetFrameSize(regBase, slaveConfig->frameSize);
    SPI_REG_SetTransmitOrder(regBase, slaveConfig->txOrder);
    SPI_REG_SetReceiveOrder(regBase, slaveConfig->rxOrder);

    /* Create the semaphore */
    errCode = OS_Adapter_CreateSemaphore(&(spiState->spiSemaphore), 0U);

    /* Enable spi interrupt */
    NVIC_EnableIRQ(s_spiIrqId[instance]);

    SPI_REG_Enable(regBase);

    OS_Adapter_Init(NULL);

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveDeinit
 *** Description   : De-Init a SPI instance
 ******************************************************************************/
errcode_t SPI_SlaveDeinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(s_spiState[instance] != NULL);

    errcode_t errCode = ERR_SUCCESS;
    spi_state_t* spiState = s_spiState[instance];
    module_names_t spiModName = s_spiModuleName[instance];
    IRQn_Type spiIrqId = s_spiIrqId[instance];

    ASSERT_PARAM(spiState->isTransferInProgress == false);

    /* Destroy the semaphore */
#ifdef USING_OS_FREERTOS
    if (spiState->spiSemaphore != NULL) {
#endif
        (void)OS_Adapter_DestroySemaphore(&(spiState->spiSemaphore));
#ifdef USING_OS_FREERTOS
        spiState->spiSemaphore = NULL;
    }
#endif

    /* Clear the state pointer */
    s_spiState[instance] = NULL;

    /* Set spi instance active */
    CLOCK_SetModuleSoftResetState(spiModName, RESET_ACTIVE);
    /* Disable module bus clock */
    CLOCK_SetModuleBusClockEnable(spiModName, false);

    /* Disable spi interrupt */
    NVIC_DisableIRQ(spiIrqId);
    NVIC_ClearPendingIRQ(spiIrqId);

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveGetDefaultConfig
 *** Description   : Get a default configuration of SPI slave
 ******************************************************************************/
void SPI_SlaveGetDefaultConfig(spi_slave_config_t* slaveConfig)
{
    ASSERT_PARAM(slaveConfig != NULL);

    slaveConfig->pinConfig = SPI_PIN_SOUT_MISO_SIN_MOSI;
    slaveConfig->csPin = SPI_CS0;
    slaveConfig->csPol = SPI_CS_POL_LOW;
    slaveConfig->clkPol = SPI_CLK_POL_LOW;
    slaveConfig->clkPha = SPI_CLK_PHA_1ST;
    slaveConfig->frameSize = SPI_FRAME_SIZE_8BIT;
    slaveConfig->txOrder = SPI_TRANSFER_ORDER_MSB;
    slaveConfig->rxOrder = SPI_TRANSFER_ORDER_MSB;
    slaveConfig->transferType = SPI_USING_INTERRUPT;
    slaveConfig->txDMAChannel = 2U;
    slaveConfig->rxDMAChannel = 3U;
    slaveConfig->callback = NULL;
    slaveConfig->callbackParam = NULL;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveTransferBlocking
 *** Description   : Start a blocking transfer for SPI slave
 ******************************************************************************/
errcode_t SPI_SlaveTransferBlocking(uint32_t instance,
                                    const uint8_t* txBuffer,
                                    uint8_t* rxBuffer,
                                    uint32_t transferSize,
                                    uint32_t timeout)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));

    errcode_t errCode = ERR_SUCCESS;
    SPI_Type* regBase = s_spiRegBase[instance];
    spi_state_t* spiState = s_spiState[instance];

    if (transferSize > 0U) {
        if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_MODULE_BUSY)) {
            errCode = ERR_BUSY;
        } else {
            /* Dummy wait to ensure the senmaphore is 0, no need to check result */
            (void)OS_Adapter_WaitSemaphore(&(spiState->spiSemaphore), 0U);
            spiState->isBlocking = true;

            errCode = SPI_SlaveTransfer(instance,
                                        txBuffer,
                                        rxBuffer,
                                        transferSize);

            if (errCode == ERR_SUCCESS) {
                errCode = OS_Adapter_WaitSemaphore(&(spiState->spiSemaphore), timeout);

                if (errCode == ERR_TIMEOUT) {
                    spiState->isBlocking = false;
                    SPI_SlaveAbortTransfer(instance);
                } else {
                    /* Disable the interrupts and clear the status flags for transmit/receive errors */
                    SPI_REG_SetIntEnable(regBase, SPI_INT_TX_UNDERFLOW, false);
                    SPI_REG_SetIntEnable(regBase, SPI_INT_RX_OVERFLOW, false);
                    SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW);
                    SPI_REG_ClearStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW);
                }
            } else {
                /* Disable interrupt requests */
                SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, false);
                SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, false);

                spiState->isBlocking = false;
            }
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveTransfer
 *** Description   : Start a non-blocking transfer for SPI slave
 ******************************************************************************/
errcode_t SPI_SlaveTransfer(uint32_t instance,
                            const uint8_t* txBuffer,
                            uint8_t* rxBuffer,
                            uint32_t transferSize)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));

    errcode_t errCode = ERR_SUCCESS;
    SPI_Type* regBase = s_spiRegBase[instance];
    spi_state_t* spiState = s_spiState[instance];
    uint32_t alignedSize = (spiState->bytesPerFrame - 1);

    if ((((uint32_t)txBuffer & alignedSize) != 0U) \
            || (((uint32_t)rxBuffer & alignedSize) != 0U) \
            || ((transferSize & alignedSize) != 0U)) {
        /* txBuffer(for M0 core) and transferSize should be frame size aligned */
        errCode = ERR_ERROR;
    } else if ((spiState->transferType == SPI_USING_DMA) &&
               (transferSize > (CONFIG_SPI_DMA_MAX_SIZE << alignedSize))) {
        /* DMA transfer count over than DMA_CHANNEL_TRANFER_MAX_SIZE is unsuported */
        errCode = ERR_UNSUPPORTED;
    } else {
        spiState->status = SPI_TRANSFER_OK;

        /* Clear fifo and all status flags */
        SPI_REG_ClearErrorStatusFlag(regBase, ((uint32_t)(SPI_STATUS_TX_UNDERFLOW) | (uint32_t)(SPI_STATUS_RX_OVERFLOW)));

        while (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            (void)SPI_REG_ReadData(regBase);
        }

        /* Enable error interrupt sources */
        SPI_REG_SetErrorIntEnable(regBase, ((uint32_t)(SPI_INT_TX_UNDERFLOW) | (uint32_t)(SPI_INT_RX_OVERFLOW)), true);

        spiState->txBuff = txBuffer;
        spiState->rxBuff = rxBuffer;
        spiState->txSize = transferSize;
        spiState->rxSize = transferSize;
        spiState->isTransferInProgress = true;

        if (spiState->transferType == SPI_USING_INTERRUPT) {
            SPI_REG_SetIntEnable(regBase, SPI_INT_TX_NOT_FULL, true);
            SPI_REG_SetIntEnable(regBase, SPI_INT_RX_NOT_EMPTY, true);
        }

        else {
            SPI_DMATransfer(instance, transferSize, txBuffer, rxBuffer);
        }

    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveTransferPolling
 *** Description   : Start a polling transfer for SPI slave
 ******************************************************************************/
errcode_t SPI_SlaveTransferPolling(uint32_t instance,
                                   const uint8_t* txBuffer,
                                   uint8_t* rxBuffer,
                                   uint32_t transferSize)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM((txBuffer != NULL) || (rxBuffer != NULL));
    ASSERT_PARAM(transferSize > 0U);

    errcode_t errCode = ERR_SUCCESS;
    spi_state_t* spiState = s_spiState[instance];
    SPI_Type* regBase = s_spiRegBase[instance];
    uint32_t alignedSize = (spiState->bytesPerFrame - 1U);

    if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_MODULE_BUSY)) {
        errCode = ERR_BUSY;
    } else if ((((uint32_t)txBuffer & alignedSize) != 0U) \
               || ((transferSize & alignedSize) != 0U)) {
        /* txBuffer(for M0 core) and transferSize should be frame size aligned */
        errCode = ERR_ERROR;
    } else {
        spiState->status = SPI_TRANSFER_OK;

        /* Clear fifo and all status flags */
        SPI_REG_ClearErrorStatusFlag(regBase, ((uint32_t)(SPI_STATUS_TX_UNDERFLOW) | (uint32_t)(SPI_STATUS_RX_OVERFLOW)));

        while (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
            (void)SPI_REG_ReadData(regBase);
        }

        spiState->txBuff = txBuffer;
        spiState->rxBuff = rxBuffer;
        spiState->txSize = transferSize;
        spiState->rxSize = transferSize;
        spiState->isTransferInProgress = true;

        do {
            /* Transmitting data */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_NOT_FULL)) {
                if (spiState->txSize > 0U) {
                    SPI_FillTxBuffer(instance);
                }
            }

            /* Receiving data */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_NOT_EMPTY)) {
                if (spiState->rxSize > 0U) {
                    SPI_ReadRxBuffer(instance);
                }
            }

            /* Check the sending fifo for underflow */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_TX_UNDERFLOW)) {
                errCode = ERR_SPI_TX_UNDERRUN;
                spiState->status = SPI_TRANSMIT_ERROR;
                break;
            }

            /* Check the receiving fifo for overflow */
            if (SPI_REG_GetStatusFlag(regBase, SPI_STATUS_RX_OVERFLOW)) {
                errCode = ERR_SPI_RX_OVERFLOW;
                spiState->status = SPI_RECEIVE_ERROR;
                break;
            }
        } while ((spiState->txSize > 0U) || (spiState->rxSize > 0U));

        SPI_CompleteTransfer(instance);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveGetTransferStatus
 *** Description   : Get transfer status in non-blocking transfer:
 ***                 in progress or complete.
 ******************************************************************************/
errcode_t SPI_SlaveGetTransferStatus(uint32_t instance, uint32_t* bytesRemained)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(bytesRemained != NULL);
    ASSERT_PARAM(s_spiState[instance] != NULL);

    spi_state_t* spiState = s_spiState[instance];
    errcode_t errCode = ERR_SUCCESS;
    uint32_t dmaRemainingCount = 0U;

    if (spiState->transferType == SPI_USING_INTERRUPT) {
        *bytesRemained = spiState->txSize;
    } else {
        dmaRemainingCount = DMA_GetRemainingCount(spiState->rxDMAChannel);
        *bytesRemained = dmaRemainingCount * spiState->bytesPerFrame;
    }

    if (spiState->status == SPI_TRANSFER_OK) {
        errCode = (spiState->isTransferInProgress) ? ERR_BUSY : ERR_SUCCESS;
    } else if (spiState->status == SPI_TRANSMIT_ERROR) {
        errCode = ERR_SPI_TX_UNDERRUN;
    } else {
        errCode = ERR_SPI_RX_OVERFLOW;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : SPI_SlaveAbortTransfer
 *** Description   : Terminates a transfer during progress.
 ******************************************************************************/
void SPI_SlaveAbortTransfer(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);

    SPI_CompleteTransfer(instance);
}

/*******************************************************************************
 *** Function Name : SPI_Install
 *** Description   : Start a blocking transfer for SPI master
 ******************************************************************************/
void SPI_InstallCallback(uint32_t instance, spi_callback_t callback, void* param)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);
    ASSERT_PARAM(s_spiState[instance] != NULL);

    spi_state_t* spiState = s_spiState[instance];

    spiState->callback = callback;
    spiState->callbackParam = param;
}

/*******************************************************************************
 *** Function Name : SPI_IRQHandlerProcess
 *** Description   : SPI interrupt handler process function
 ******************************************************************************/
void SPI_IRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPI_INSTANCE_COUNT);

    SPI_Type* regBase = s_spiRegBase[instance];

    if (SPI_REG_IsMaster(regBase)) {
        SPI_MasterIRQHandlerProcess(instance);
    } else {
        SPI_SlaveIRQHandlerProcess(instance);
    }
}

/*******EOF********************************************************************/

